﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Chart3D;
using System.Windows.Forms;
using System.Drawing;
using PGMHelper;
using System.Runtime.Serialization;

namespace WSAP
{
    /// <summary>
    /// WModelNet拓展方法
    /// </summary>
    public static class WModelNetExtension
    {
        #region Chart3DMain

        /// <summary>
        /// 生成三维模型
        /// </summary>
        /// <param name="model"></param>
        /// <param name="control"></param>
        /// <param name="factor"></param>
        /// <returns></returns>
        public static Chart3DMain GetChart3D(this WModelNet model, Control control, StructrueChartParam param)
        {
            return model.GetChart3D(control, param, -1);
        }

        /// <summary>
        /// 生成三维模型
        /// </summary>
        /// <param name="model"></param>
        /// <param name="control"></param>
        /// <param name="param"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static Chart3DMain GetChart3D(this WModelNet model, Control control, StructrueChartParam param, int index)
        {
            //判断类型
            switch (param.ChartType)
            {
                case StructureChartType.Eigen:
                    return model.GetModeChart3D(control, StructrueChart.ModeID, param[index]);
                case StructureChartType.GeneralStatic:
                    return model.GetStaticChart3D(control, param[index]);
                case StructureChartType.SlabOnly:
                    return model.GetSlabChart3D(control);
            }
            //返回绘图数据
            return null;
        }

        /// <summary>
        /// 生成三维模型
        /// </summary>
        /// <param name="model"></param>
        /// <param name="control"></param>
        /// <returns></returns>
        private static Chart3DMain GetStaticChart3D(this WModelNet model, Control control, double factor)
        {
            //初始化
            var chart3D = new Chart3DMain(control, isColorMap: false, type: AxisBoundType.Building);
            //设定透明度
            chart3D.DrawChart.SetColorMap(80);
            //获得振型向量最大值
            var maxValue = model.GetMaxNodeResponse();
            //假设位移为零（两端约束，则假设挠度为1m）
            if (maxValue == 0) maxValue = 1E3;
            //乘以系数
            factor *= model.GetMaxDimention() * StructrueChart.MaxDeformToMaxDimention / maxValue;
            //遍历单元
            model.GetElements().ForEach(element =>
            {
                //基本模型
                var data = element.GetData(1);
                if (data != null) chart3D.Add(data);
                //基本模型
                var data2 = element.GetData(factor);
                if (data2 != null) chart3D.Add(data2);
            });
            //返回绘图数据
            return chart3D;
        }

        /// <summary>
        /// 生成三维模型
        /// </summary>
        /// <param name="model"></param>
        /// <param name="control"></param>
        /// <param name="modeID"></param>
        /// <param name="factor"></param>
        /// <returns></returns>
        private static Chart3DMain GetModeChart3D(this WModelNet model, Control control, int modeID, double factor)
        {
            //初始化
            var chart3D = new Chart3DMain(control, isColorMap: false, type: AxisBoundType.Building);
            //设定透明度
            chart3D.DrawChart.SetColorMap(80);
            //获得振型向量最大值
            var maxValue = model.GetMaxAbsEigenVertorValue(modeID);
            //假设位移为零（两端约束，则假设挠度为1m）
            if (maxValue == 0) maxValue = 1E3;
            //乘以系数
            factor *= model.GetMaxDimention() * StructrueChart.MaxDeformToMaxDimention / maxValue;
            //遍历单元
            model.GetElements().ForEach(element =>
            {
                //基本模型
                var data = element.GetData(1);
                if (data != null) chart3D.Add(data);
                //基本模型
                var data2 = element.GetData(modeID, factor);
                if (data2 != null) chart3D.Add(data2);
            });
            //返回绘图数据
            return chart3D;
        }

        /// <summary>
        /// 生成板模型
        /// </summary>
        /// <param name="model"></param>
        /// <param name="control"></param>
        /// <returns></returns>
        private static Chart3DMain GetSlabChart3D(this WModelNet model, Control control)
        {
            //底色修改
            control.BackColor = Color.White;
            //初始化
            var chart3D = new Chart3DMain(control, isColorBar: true, type: AxisBoundType.XYEqual, isInterp: true, interpNumber: 10);
            //设定透明度
            chart3D.DrawChart.SetColorMap(150);
            //遍历单元
            model.GetElements().ForEach(element =>
            {
                //基本模型
                var data = element.GetData(1);
                if (data != null) chart3D.Add(data);
            });
            //返回绘图数据
            return chart3D;
        }

        #endregion

        /// <summary>
        /// 获得响应最大的节点
        /// </summary>
        /// <param name="node"></param>
        /// <param name="responseType"></param>
        /// <returns></returns>
        public static WNodeNet GetMaxResponseNode(this WModelNet model, NodeResponseEnum responseType, int dof)
        {
            //记录节点指针
            WNodeNet node = null;
            //遍历单元
            model.GetElements().ForEach(element => node = element.GetMaxResponseNode(node, responseType, dof));
            return node;
        }

        /// <summary>
        /// 获得响应最大的节点
        /// </summary>
        /// <param name="node"></param>
        /// <param name="responseType"></param>
        /// <returns></returns>
        private static WNodeNet GetMaxResponseNode(this WModelNet model, NodeResponseEnum responseType, int dof, double Z)
        {
            //记录节点指针
            WNodeNet node = null;
            //遍历单元
            model.GetElements().ForEach(element =>
            {
                    node = element.GetMaxResponseNode(node, responseType, dof, Z);
            });
            return node;
        }

        /// <summary>
        /// 获得响应最大的节点
        /// </summary>
        /// <param name="node"></param>
        /// <param name="responseType"></param>
        /// <returns></returns>
        public static double GetMaxNodeResponse(this WModelNet model)
        {
            return (new List<double> {
                Math.Abs(model.GetMaxResponseNode(NodeResponseEnum.Disp, 1).GetResponse(NodeResponseEnum.Disp, 1)),
                Math.Abs(model.GetMaxResponseNode(NodeResponseEnum.Disp, 2).GetResponse(NodeResponseEnum.Disp, 2)),
                Math.Abs(model.GetMaxResponseNode(NodeResponseEnum.Disp, 3).GetResponse(NodeResponseEnum.Disp, 3)),
            }).Max();
        }

        /// <summary>
        /// 获得响应最大的节点
        /// </summary>
        /// <param name="node"></param>
        /// <param name="responseType"></param>
        /// <returns></returns>
        public static double GetMaxNodeResponse(this WModelNet model, double Z)
        {
            return (new List<double> {
                Math.Abs(model.GetMaxResponseNode(NodeResponseEnum.Disp, 1, Z).GetResponse(NodeResponseEnum.Disp, 1)),
                Math.Abs(model.GetMaxResponseNode(NodeResponseEnum.Disp, 2, Z).GetResponse(NodeResponseEnum.Disp, 2)),
                Math.Abs(model.GetMaxResponseNode(NodeResponseEnum.Disp, 3, Z).GetResponse(NodeResponseEnum.Disp, 3)),
            }).Max();
        }

        /// <summary>
        /// 获得模型最大尺度
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static double GetMaxDimention(this WModelNet model)
        {
            //初始坐标向量
            var xs = new List<double>();
            var ys = new List<double>();
            var zs = new List<double>();
            //遍历单元
            model.GetElements().ForEach(element =>
            {
                var tuple = element.GetPosList();
                xs.AddRange(tuple.Item1);
                ys.AddRange(tuple.Item2);
                zs.AddRange(tuple.Item3);
            });
            return (new List<double> {
                xs.Max() - xs.Min(),
                  ys.Max() - ys.Min(),
                    zs.Max() - zs.Min(),
            }).Max();
        }

        /// <summary>
        /// 获得节点响应
        /// </summary>
        /// <param name="model"> 分析模型 </param>
        /// <param name="responseType"> 响应类型 </param>
        /// <param name="dof"> 自由度 </param>
        /// <returns></returns>
        public static double GetNodeResponse(this WModelNet model, int nodeTag, NodeResponseEnum responseType, int dof)
        {
            return Math.Round(model.GetNodeResponse(nodeTag, (int)responseType, dof), 4);
        }

        /// <summary>
        /// 获得节点响应
        /// </summary>
        /// <param name="model"> 分析模型 </param>
        /// <param name="responseType"> 响应类型 </param>
        /// <param name="dof"> 自由度 </param>
        /// <returns></returns>
        public static List<double> GetNodeResponseList(this WModelNet model, int nodeTag, NodeResponseEnum responseType, int dof)
        {
            return model.GetNodeResponseList(nodeTag, (int)responseType, dof);
        }

        /// <summary>
        ///  获得振型向量最大值
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public static double GetMaxAbsEigenVertorValue(this WModelNet model, int modeID)
        {
            var valueList = (from element in model.GetElements() select element.GetMaxAbsEigenVertorValue(modeID)).ToList();
            return valueList.Max();
        }

        #region ToGraphics

        /// <summary>
        /// 绘图
        /// </summary>
        /// <param name="model"></param>
        /// <param name="param"></param>
        public static void ToGraphics(this WModelNet model, StructrueChartParam param)
        {
            //实例化窗体
            var chartForm = new ModelVisible(model, param);
            //窗体显示
            model.BasicShow(chartForm, param);
        }

        /// <summary>
        /// 生成动态图
        /// </summary>
        /// <param name="model"></param>
        /// <param name="param"></param>
        /// <param name="chartType"></param>
        public static void ToDynamicGraphics(this WModelNet model, StructrueChartParam param)
        {
            //实例化窗体
            var chartForm = new ModelVisibleDynamic(model, param);
            //显示警告及错误信息
            model.ShowWarningError();
            //窗体显示
            if (param.isDialog) chartForm.ShowDialog();
            else chartForm.Show();
        }

        /// <summary>
        /// 窗体显示
        /// </summary>
        /// <param name="model"></param>
        /// <param name="chartForm"></param>
        /// <param name="param"></param>
        private static void BasicShow(this WModelNet model, ModelVisible chartForm, StructrueChartParam param)
        {
            //显示警告及错误信息
            model.ShowWarningError();
            //是否显示最大节点响应
            if (param.isNodeMaxResponseShow)
            {
                //获得目标节点
                var node = model.GetMaxResponseNode(param.ResponseType, param.DOF);
                //非模态窗体
                MessageBoxExtern.Asterisk(node.GetMaxResponseDescp(param.ResponseType, param.DOF), isDialog: false);
            }
            //窗体显示
            if (param.isDialog) chartForm.ShowDialog();
            else chartForm.Show();
        }

        #endregion

        #region Error Warning

        /// <summary>
        /// 显示警告及错误信息
        /// </summary>
        /// <param name="model"></param>
        public static bool ShowWarningError(this WModelNet model)
        {
            //警告及错误信息不存在
            if (model.GetError() == string.Empty && model.GetWarning() == string.Empty)
                return true;
            //存在则显示
            Form f = new AnalyzeInfo(model);
            f.ShowDialog();
            return false;
        }

        /// <summary>
        /// 获得错误信息
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static string GetError(this WModelNet model)
        {
            //初始化
            var error = string.Empty;
            //遍历错误信息
            model.GetErrors().ForEach(str => error += str + Environment.NewLine);
            return error;
        }

        /// <summary>
        /// 获得警告信息
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static string GetWarning(this WModelNet model)
        {
            //初始化
            var error = string.Empty;
            //遍历错误信息
            model.GetWarnings().ForEach(str => error += str + Environment.NewLine);
            return error;
        }

        #endregion

        #region 梁荷载

        /// <summary>
        /// 增加梁线荷载
        /// </summary>
        /// <param name="model"></param>
        /// <param name="elementTag"></param>
        /// <param name="gravityLoad"></param>
        /// <returns></returns>
        public static bool AddBeamUniformLoad(this WModelNet model, int elementTag, 
            double gravityUniformLoad)
        {
            return model.AddElementLoad(elementTag, (int)ElementLoadEnum.FrameUniform, 
                new List<double> { 0, -gravityUniformLoad, 0 });
        }

        /// <summary>
        /// 增加梁线荷载
        /// </summary>
        /// <param name="model"></param>
        /// <param name="elementTag"></param>
        /// <param name="gravityLoad"></param>
        /// <returns></returns>
        public static bool AddBeamTriangularLoad(this WModelNet model, int elementTag, 
            double gravityMaxLoad, double maxLoadLengthFacotr)
        {
            return model.AddElementLoad(elementTag, (int)ElementLoadEnum.FrameTriangular,
                new List<double> { 0, -gravityMaxLoad, maxLoadLengthFacotr });
        }

        /// <summary>
        /// 增加梁线荷载
        /// </summary>
        /// <param name="model"></param>
        /// <param name="elementTag"></param>
        /// <param name="gravityLoad"></param>
        /// <returns></returns>
        public static bool AddBeamTrapezoidLoad(this WModelNet model, int elementTag,
            double gravityMaxLoad, double maxLoadLengthFacotrI, double maxLoadLengthFacotrJ)
        {
            return model.AddElementLoad(elementTag, (int)ElementLoadEnum.FrameTrapezoid,
                new List<double> { 0, -gravityMaxLoad, maxLoadLengthFacotrI, maxLoadLengthFacotrJ });
        }

        /// <summary>
        /// 增加梁线荷载
        /// </summary>
        /// <param name="model"></param>
        /// <param name="elementTag"></param>
        /// <param name="gravityLoad"></param>
        /// <returns></returns>
        public static bool AddBeamPointLoad(this WModelNet model, int elementTag,
            double gravityLoad, double loadLengthFacotr)
        {
            return model.AddElementLoad(elementTag, (int)ElementLoadEnum.FramePoint,
                new List<double> { 0, -gravityLoad, 0, loadLengthFacotr });
        }

        #endregion

        /// <summary>
        /// 单元数目
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static int GetElementCount(this WModelNet model)
        {
            return model.GetElements().Count;
        }

        /// <summary>
        /// 获得单元
        /// </summary>
        /// <param name="model"> 分析模型 </param>
        /// <param name="tag"> 单元编号 </param>
        /// <param name="element"> 单元 </param>
        /// <returns></returns>
        public static bool GetElement(this WModelNet model, int tag, out WElementNet element)
        {
            //获得单元
            element = model.GetElement(tag);
            //判断是否获得成功
            return element.GetTag() != -1;
        }

        /// <summary>
        /// 获得节点
        /// </summary>
        /// <param name="model"> 分析模型 </param>
        /// <param name="tag"> 单元编号 </param>
        /// <param name="node"> 单元 </param>
        /// <returns></returns>
        public static bool GetNode(this WModelNet model, int tag, out WNodeNet node)
        {
            //获得单元
            node = model.GetNode(tag);
            //判断是否获得成功
            return node.GetTag() != -1;
        }
    }

    /// <summary>
    /// 辅助方法
    /// </summary>
    public static class WSAPHelper
    {
        /// <summary>
        /// 获得响应描述
        /// </summary>
        /// <param name="responseType"></param>
        /// <returns></returns>
        public static string GetResponseDescp(NodeResponseEnum responseType)
        {
            switch(responseType)
            {
                case NodeResponseEnum.Disp: return "位移";
                case NodeResponseEnum.Vel: return "速度";
                case NodeResponseEnum.Accel: return "加速度";
                default: return string.Empty;
            }
        }

        /// <summary>
        /// 插值获得位移响应
        /// </summary>
        /// <param name="dispI"></param>
        /// <param name="dispJ"></param>
        /// <param name="rotationI"></param>
        /// <param name="rotationJ"></param>
        /// <param name="length"></param>
        /// <param name="dist"></param>
        /// <returns></returns>
        public static double GetInterpolationPointsDisp(double dispI, double dispJ, double rotationI, double rotationJ, double factor)
        {
            //系数求解
            double d = dispI;
            double c = rotationI;
            //公式I的系数Ai Bi
            double Ai = 1;
            double Bi = 1;
            double Ci = dispJ - d - c ;
            //公式J
            double Aj = 3 * 1;
            double Bj = 2 * 1;
            double Cj = rotationJ - c;
            //求得a
            double a = (Ci * Bj - Cj * Bi) / (Ai * Bj - Aj * Bi);
            //求得b
            double b = (Ci - a * Ai) / Bi;
            //求得响应
            return Math.Pow(factor, 3) * a + Math.Pow(factor, 2) * b + factor * c + d;
        }

        /// <summary>
        /// 显示周期信息
        /// </summary>
        /// <param name="periodList"></param>
        public static void ShowPeriodInfo(List<double> periodList)
        {
            //初始化
            var info = string.Empty;
            //遍历周期
            for (int index = 0; index < periodList.Count; index++)
                info += string.Format("第{0}周期：{1}s\n", index + 1, 
                    periodList[index].ToString("f4"));
            //显示周期
            MessageBoxExtern.Asterisk(info, isDialog: false);
        }
    }
}
